package com.chenwy.example.configuration;

import com.chenwy.example.pojo.AdditionalModel;
import com.fasterxml.classmate.TypeResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.async.DeferredResult;
import springfox.documentation.builders.*;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.*;
import java.lang.reflect.WildcardType;
import java.time.LocalDate;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.AlternateTypeRules.*;

@Configuration
public class SwaggerConfiguration {

    @Autowired
    private TypeResolver typeResolver;

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)//Docket, Springfox’s, primary api configuration mechanism is initialized for swagger specification 2.0
                .apiInfo(apiInfo())
                .select()// returns an instance of ApiSelectorBuilder to give fine grained control over the endpoints exposed via swagger.
                .apis(RequestHandlerSelectors.any())//allows selection of RequestHandler's using a predicate. The example here uses an any predicate (default). Out of the box predicates provided are any, none, withClassAnnotation, withMethodAnnotation and basePackage
                .paths(PathSelectors.any())//allows selection of Path's using a predicate. The example here uses an any predicate (default). Out of the box we provide predicates for regex, ant, any, none
                .build()//The selector needs to be built after configuring the api and path selectors.
                .pathMapping("/")//Adds a servlet path mapping, when the servlet has a path mapping. This prefixes paths with the provided path mapping.
                .directModelSubstitute(LocalDate.class, String.class)//Convenience rule builder that substitutes LocalDate with String when rendering model properties Convenience rule builder that substitutes a generic type with one type parameter with the type parameter.
                .genericModelSubstitutes(ResponseEntity.class)
                .alternateTypeRules(
                        newRule(typeResolver.resolve(DeferredResult.class,
                                typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
                                typeResolver.resolve(WildcardType.class)))//In this example ResponseEntity<T> with T. alternateTypeRules allows custom rules that are a bit more involved. The example substitutes DeferredResult<ResponseEntity<T>> with T generically.
                .useDefaultResponseMessages(false)//Flag to indicate if default http response codes need to be used or not.
                .globalResponseMessage(RequestMethod.GET,//Allows globally overriding response messages for different http methods. In this example we override the 500 error code for all GET requests …​
                        newArrayList(new ResponseMessageBuilder()
                                .code(500)
                                .message("500 message")
                                .responseModel(new ModelRef("Error"))//	…​ and indicate that it will use the response model Error (which will be defined elsewhere)
                                .build()))
                .securitySchemes(newArrayList(apiKey()))//Sets up the security schemes used to protect the apis. Supported schemes are ApiKey, BasicAuth and OAuth
                .securityContexts(newArrayList(securityContext()))//Provides a way to globally set up security contexts for operation. The idea here is that we provide a way to select operations to be protected by one of the specified security schemes.
                .enableUrlTemplating(true)//An example of this would be two apis: First, http://example.org/findCustomersBy?name=Test to find customers by name. Per RFC 6570, this would be represented as http://example.org/findCustomersBy{?name}. Second, http://example.org/findCustomersBy?zip=76051 to find customers by zip. Per RFC 6570, this would be represented as http://example.org/findCustomersBy{?zip}.
//                .globalOperationParameters(//Allows globally configuration of default path-/request-/headerparameters which are common for every rest operation of the api, but aren`t needed in spring controller method signature (for example authenticaton information). Parameters added here will be part of every API Operation in the generated swagger specification. on how the security is setup the name of the header used may need to be different. Overriding this value is a way to override the default behavior.
//                        newArrayList(new ParameterBuilder()
//                                .name("someGlobalParameter")
//                                .description("Description of someGlobalParameter")
//                                .modelRef(new ModelRef("string"))
//                                .parameterType("query")
//                                .required(true)
//                                .build()))
//                .tags(new Tag("Pet Service", "All apis relating to pets"))//Adding tags is a way to define all the available tags that services/operations can opt into. Currently this only has name and description.
                .additionalModels(typeResolver.resolve(AdditionalModel.class))//Are there models in the application that are not "reachable"? Not reachable is when we have models that we would like to be described but aren’t explicitly used in any operation. An example of this is an operation that returns a model serialized as a string. We do want to communicate the expectation of the schema for the string. This is a way to do exactly that.
                ;
    }

    private ApiKey apiKey() {
        return new ApiKey("mykey", "api_key", "header");//Here we use ApiKey as the security schema that is identified by the name mykey
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex("/anyPath.*"))//Selector for the paths this security context applies to.
                .build();
    }

    List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope
                = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return newArrayList(new SecurityReference("mykey", authorizationScopes));//Here we use the same key defined in the security scheme mykey
    }

    @Bean
    SecurityConfiguration security() {
        return SecurityConfigurationBuilder.builder()//	Optional swagger-ui security configuration for oauth and apiKey settings
                .clientId("test-app-client-id")
                .clientSecret("test-app-client-secret")
                .realm("test-app-realm")
                .appName("test-app")
                .scopeSeparator(",")
                .additionalQueryStringParams(null)
                .useBasicAuthenticationWithAccessCodeGrant(false)
                .build();
    }

    @Bean
    UiConfiguration uiConfig() {
        return UiConfigurationBuilder.builder()//Optional swagger-ui ui configuration currently only supports the validation url * Incubating * setting this flag signals to the processor that the paths generated should try and use form style query expansion. As a result we could distinguish paths that have the same path stem but different query string combinations.
                .deepLinking(true)
                .displayOperationId(false)
                .defaultModelsExpandDepth(1)
                .defaultModelExpandDepth(1)
                .defaultModelRendering(ModelRendering.EXAMPLE)
                .displayRequestDuration(false)
                .docExpansion(DocExpansion.NONE)
                .filter(false)
                .maxDisplayedTags(null)
                .operationsSorter(OperationsSorter.ALPHA)
                .showExtensions(false)
                .tagsSorter(TagsSorter.ALPHA)
                .supportedSubmitMethods(UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS)
                .validatorUrl(null)
                .build();
    }

    ApiInfo apiInfo(){

        return new ApiInfoBuilder()
                .title("我的接口文档")
                .contact(new Contact("chenwy","",""))
                .description("接口文档描述")
                .build();
    }

}
